/**
  ******************************************************************************
  * @file    flash.c
  * @brief   This file provides code for the configuration
  *          of the SPI instances.
  ******************************************************************************
  * @attention
  *
  * @Author: 中国赛宝 CEPREI
  * @Date: 
  * @Version: 1.0.001
  * All rights reserved.
  *
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include <SPI.h>

/* Defines -------------------------------------------------------------------*/
#define VSPI_MISO   MISO        //MISO = 19
#define VSPI_MOSI   MOSI        //MOSI = 23
#define VSPI_SCLK   SCK         //SCLK = 18
#define VSPI_SS     SS          //CS = 5
#define VSPI_CLK    100000      //100kHz

/* Variables -----------------------------------------------------------------*/
uint16_t FLASH_ChipId = 0;
uint8_t FLASH_ReadBuff[32] = {0};
uint8_t FLASH_WriteBuff[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
/* 华邦flash芯片型号ID定义 */
enum
{
    W25Q80_ChipId = 0XEF13,
    W25Q16_ChipId = 0XEF14,
    W25Q32_ChipId = 0XEF15,
    W25Q64_ChipId = 0XEF16,
    W25Q128_ChipId = 0XEF17,
    W25Q256_ChipId = 0XEF18,
};
/* 华邦flash芯片指令定义 */
enum
{
    FLASH_WriteEnableCmd = 0x06,
    FLASH_WriteDisableCmd = 0x04,
    FLASH_ReadStatusReg1Cmd = 0x05,
    FLASH_ReadStatusReg2Cmd = 0x35,
    FLASH_ReadStatusReg3Cmd = 0x15,
    FLASH_WriteStatusReg1Cmd = 0x01,
    FLASH_WriteStatusReg2Cmd = 0x31,
    FLASH_WriteStatusReg3Cmd = 0x11,
    FLASH_ReadDataCmd = 0x03,
    FLASH_FastReadDataCmd = 0x0B,
    FLASH_FastReadDualCmd = 0x3B,
    FLASH_PageProgramCmd = 0x02,
    FLASH_BlockEraseCmd = 0xD8,
    FLASH_SectorEraseCmd = 0x20,
    FLASH_ChipEraseCmd = 0xC7,
    FLASH_PowerDownCmd = 0xB9,
    FLASH_ReleasePowerDownCmd = 0xAB,
    FLASH_DeviceIDCmd = 0xAB,
    FLASH_ManufactDeviceIDCmd = 0x90,
    FLASH_JedecDeviceIDCmd = 0x9F,
    FLASH_Enable4ByteAddrCmd = 0xB7,
    FLASH_Exit4ByteAddrCmd = 0xE9,
};

SPIClass * FLASH_Spi = NULL;

/* Prototypes ----------------------------------------------------------------*/

/* Functions -----------------------------------------------------------------*/
/* 读取flash的ID号 */
static uint16_t FLASH_ReadId(SPIClass *spi)
{
  uint16_t value = 0;
  digitalWrite(spi->pinSS(), LOW); 
  spi->transfer(0x90);
  spi->transfer(0x00);
  spi->transfer(0x00);
  spi->transfer(0x00);
  value = (uint16_t)(spi->transfer(0x00))<<8;
  value += spi->transfer(0x00);
  digitalWrite(spi->pinSS(), HIGH); 
  spi->endTransaction();
  return value;
}
/* 读取flash内容 */
static void FLASH_Read(SPIClass *spi,uint8_t *buff,uint32_t addr,uint16_t num)
{
  uint16_t i = 0;
  digitalWrite(spi->pinSS(),LOW); 
  spi->transfer(FLASH_ReadDataCmd);
  spi->transfer((uint8_t)((addr)>>16));
  spi->transfer((uint8_t)((addr)>>8));
  spi->transfer((uint8_t)((addr)));
  for(i=0;i<num;i++)
	{ 
    buff[i]= spi->transfer(0x00);
  }
  digitalWrite(spi->pinSS(), HIGH); 
  spi->endTransaction();
}

/* 写使能 */
static void FLASH_WriteEnable(SPIClass *spi)   
{
  digitalWrite(spi->pinSS(), LOW); 
  spi->transfer(FLASH_WriteEnableCmd);
  digitalWrite(spi->pinSS(), HIGH); 
  spi->endTransaction();
} 

/* 读SR状态寄存器，共3个,0x05,0x35,0x15 */
static uint8_t FLASH_ReadSR(SPIClass *spi,uint8_t regno)   
{
	uint8_t byte=0,command=0; 
    switch(regno)
    {
        case 1:
            command=FLASH_ReadStatusReg1Cmd;    //读状态寄存器1指令
            break;
        case 2:
            command=FLASH_ReadStatusReg2Cmd;    //读状态寄存器2指令
            break;
        case 3:
            command=FLASH_ReadStatusReg3Cmd;    //读状态寄存器3指令
            break;
        default:
            command=FLASH_ReadStatusReg1Cmd;    //读状态寄存器1指令
            break;
    }    
  digitalWrite(spi->pinSS(), LOW); 
  spi->transfer(command);
	byte=spi->transfer(0);
  digitalWrite(spi->pinSS(), HIGH); 
  spi->endTransaction();
	return byte;   
}

/* 等待FLASH写结束 */
static void FLASH_WaitEnd(SPIClass *spi)
{   
	while((FLASH_ReadSR(spi,1)&0x01)==0x01);
}

/* FLASH擦除扇区 */
static void FLASH_EraseSector(SPIClass *spi,uint32_t addr)   
{  
  addr*=4096;
  FLASH_WriteEnable(spi); 
  FLASH_WaitEnd(spi);
  digitalWrite(spi->pinSS(), LOW); 
  spi->transfer(FLASH_SectorEraseCmd);
  spi->transfer((uint8_t)((addr)>>16));
  spi->transfer((uint8_t)((addr)>>8));
  spi->transfer((uint8_t)((addr)));
  digitalWrite(spi->pinSS(), HIGH); 
  spi->endTransaction();
  FLASH_WaitEnd(spi);
}

/* 写入flash内容 */
static void FLASH_Write(SPIClass *spi,uint8_t *buff,uint32_t addr,uint16_t num)
{
  uint16_t i = 0;
  FLASH_EraseSector(spi,addr/4096);
  FLASH_WriteEnable(spi);
  digitalWrite(spi->pinSS(),LOW); 
  spi->transfer(FLASH_PageProgramCmd);
  spi->transfer((uint8_t)((addr)>>16));
  spi->transfer((uint8_t)((addr)>>8));
  spi->transfer((uint8_t)((addr)));
  for(i=0;i<num;i++)
    spi->transfer((uint8_t)((buff[i])));
  digitalWrite(spi->pinSS(), HIGH); 
  spi->endTransaction();
  FLASH_WaitEnd(spi);
}

/* 示例程序FLASH初始化 */
void DemoFLASHInit(void)
{
  FLASH_Spi = new SPIClass(VSPI);
  //SCLK = 18, MISO = 19, MOSI = 23, SS = 5
  FLASH_Spi->begin();
  //doesn't handle automatically pulling CS low
  pinMode(FLASH_Spi->pinSS(), OUTPUT); //VSPI SS
  FLASH_Spi->beginTransaction(SPISettings(VSPI_CLK, MSBFIRST, SPI_MODE0));
  FLASH_Write(FLASH_Spi,FLASH_WriteBuff,0,16);
}
/* DemoFLASH读写例程 */
void DemoFLASH(void)
{
  uint8_t i = 0;
  FLASH_ChipId = FLASH_ReadId(FLASH_Spi);
  Serial.print("FLASH芯片编号:");
  Serial.print(FLASH_ChipId,HEX);
  Serial.print("\n");
  FLASH_Read(FLASH_Spi,FLASH_ReadBuff,0,32);
  Serial.print("FLASH缓存数据:");
  for(i=0;i<sizeof(FLASH_ReadBuff);i++)
  {
    Serial.print(FLASH_ReadBuff[i],HEX);
  }
  Serial.print("\n");
}

void setup() {
    Serial.begin(9600);
    DemoFLASHInit();
}
void loop() {
  DemoFLASH();
  delay(100);
}